import os
import time
import json
import pandas
import pymysql
import unittest
import requests
from datetime import datetime
from datetime import timedelta
from random import choice,randint
from openpyxl import load_workbook as lw
from dateutil.relativedelta import relativedelta


class PublicCqjy(unittest.TestCase):
    env = 'test'
    version = None
    randint = randint(1, 1000)
    session_file = 'C:\Sessionid&Token.json'
    import_asset_file = 'C:\\资产导入模板.xlsx'
    now_str = datetime.now().strftime('%Y%m%d%H%M%S')
    asset_photo = [r'C:\Users\Administrator\Pictures\2.jpg'] #资产图片
    idcard_files = [r'C:\Users\Administrator\Pictures\2.jpg']  #身份证正反面
    contract_file = [r'C:\Users\Administrator\Pictures\12.jpg']#补录/上传合同附件
    vote_change_project_file = [r'C:\Users\Administrator\Pictures\12.jpg']#表决转立项上传附件
    env_config = {
        'test': {
            'base_url': 'https://cqjy-test.b2bwings.com',
            'phones': [15016528850, 15016528855, 15016528831, 13411708804]
        },
        'uat': {
            'base_url': 'https://cqjy-uat.b2bwings.com',
            'phones': [15000000002, 15000000003, 15016528831, 13411708804]
        },
        'pro': {
            'base_url': 'https://cqjy.gdagri.gov.cn',
            'phones': [15000000003, 15000000004, 15016528831, 13000000005]
        }
    }

    def post(self, api, data, phone, account_type):
        """
        公共方法
        api: API端点路径
        data: 请求参数，字典格式
        account_type: 账号类型，0村,1镇,2个人,3法人,4村民
        phone: <5是登陆phones第几个账号(否则按传入的手机号登录)
        """
        if phone < 5:
            phone = self.env_config[self.env]['phones'][phone]
        url = self.env_config[self.env]['base_url'] + api
        sessionid = self.get_sessionid(phone, account_type, reget=0)
        for i in range(2):
            # 村和镇账号登陆时head
            head = {
                    'channel': 'admin',
                    'Connection': 'close',
                    'version': self.version,
                    'sessionid': sessionid,
                    'Content-Type': 'application/json'
                    }
            # 交易大厅个人和法人账号登陆时head
            if account_type in (2,3):
                head.update({'channel': None})
            # 村务易账号登录时head
            elif account_type == 4:
                head.update({'channel': 'census'})
            res = requests.post(verify = False,headers = head,url = url,json = data).json()
            # 如报错:需要登录/数据异常，reget重新赋值1并循环执行接口
            if res['message'] in ('需要登录', '数据异常，请联系管理员。'):
                sessionid = self.get_sessionid(phone, account_type, reget=1)
            else:
                break
        self.assertEqual(res['message'], '操作成功', f'接口报错：{url, data, res}')
        return res

    def get_sessionid(self, phone, account_type, reget):
        '''公共方法,phone：手机号码account_type：账号类型（0村，1镇，2个人，3法人，4村民）reget=1获取新sessionid(0是读文件中的sessionid)'''
        self.assertTrue(account_type < 5, "账号类型只能是0-4请核对是否正确")
        base_url = self.env_config[self.env]['base_url']
        login_data = {"phone": phone, "code": "888888"}
        sessionid_key = f'{base_url, phone, account_type}'
        if reget==1:
            if self.env == 'pro' and account_type>1:
                sessionid = input('生产环境个人/法人/村民账号登录无法绕过图形验证码，需手动输入sessionid:')
            else:
                # 测试和uat验证码都是默认888888，生产环境后台账号则需调用接口去获取验证码
                if base_url == 'https://cqjy.gdagri.gov.cn':
                    login_data.update({"code": self.get_code(phone)})
                if account_type == 0:
                    url = base_url + '/api/admin/v1/sysCvUser/open/cvLoginByCode'
                elif account_type == 1:
                    url = base_url + '/api/admin/v1/sysUser/open/loginByCode'
                elif account_type in (2,3):
                    url = base_url + '/api/user/v1/user/open/loginByCode'
                    login_data.update({"userType": account_type - 1})
                elif account_type == 4:
                    url = base_url + '/api/user/v1/user/open/censusLoginByCode'
                    login_data.update({"loginType": 1,"userType": 1})
                res = requests.post(url=url,verify=False,json=login_data,headers = {"Content-Type": "application/json"}).json()
                self.assertIn('成功', res['message'], f'手机号：{phone},账号类型：{account_type}')
                sessionid = res['data']['sessionid']
            #读取json文件并追加sessionid，如文件不存在或内容非json格式则新增文件并写入sessionid
            update_session = {sessionid_key: sessionid} #需要追加的内容
            try:
                with open(self.session_file, 'r', encoding='utf-8') as f:
                    json_data = json.load(f)
                    json_data.update(update_session)
            except (FileNotFoundError, json.JSONDecodeError):
                json_data = update_session
            with open(self.session_file, 'w', encoding='utf-8') as f:
                json.dump(json_data, f, ensure_ascii=False, indent=4)
        else:
            try:
                with open(self.session_file, 'r') as f:
                    sessionid = json.load(f)[sessionid_key]
            except:
                sessionid = self.get_sessionid(phone, account_type, reget=1)
        return sessionid

    def get_code(self,phone):
        '''公共方法：生产环境获取短信验证码'''
        head = {'Content-Type': 'application/json'}
        url = 'https://cqjy.gdagri.gov.cn/api/admin/v1/sysUser/open/loginByPassword'
        data = {
            "phone": "c63abb5dc92e28287470610b3156e77d9f39cad3853d34cbf2df4cf28500390ad2aeb8e54e8d43d36ffe41a84e0ad7f7bedf66dc1c5e879e9a9ce0ce976710819a5963931a7f6e44535f829a086c5a1e5f30c74fb91f2e25dafcfb817442fce574275ffe65939882b48c24",
            "password": "359ae8922ca7bfa8e11f0c8cc7d3bafb97e0ddb7dadf08bda9d7e1b1edfd319723cb6cb03acef8c8c45741a22496c22eca6c67614f0e4ee4954f6e9b68df745c5bccf251532ad5da7db1ac707f32fa0e52b4cdee9d846484046282072d080f443985998d73bad1f047dbb78f043921e09c5f516cd327bd9d3ee32ffaf3cccf9cfe522f17985af6bd43da482a5e39f9eb09b3350b591d648c12d72f821ac9ea13"
        }
        sessionid = requests.post(url=url, json=data, headers=head, verify=False).json()['data']['sessionid']
        head.update({'sessionid': sessionid, 'channel': 'admin', 'Connection': 'close'})
        # 点击获取验证码
        api1 = 'https://cqjy.gdagri.gov.cn/api/admin/v1/sysUser/open/LOGIN/captcha'
        data1 = {
                "key": 1745219868087,
                "phone": phone,
                "isTrusted": True,
                "_vts": 1745219883034
                }
        requests.post(url=api1,json=data1,headers=head,verify=False).json()
        # 按手机号搜索验证码
        api2 = 'https://cqjy.gdagri.gov.cn/api/message/v1/msgLog/getMsgLog'
        data2 = {"phone": phone}
        res2 = requests.post(url=api2, json=data2, headers=head, verify=False).json()
        self.assertNotEqual(res2['data']['records'],[],'没有查到验证码，请确认是否已经点击获取验证码或者手机号码是否正确！')
        #查看验证码
        api3 = 'https://cqjy.gdagri.gov.cn/api/message/v1/msgLog/getMsgCode'
        data3 = {"msgLogId": res2['data']['records'][0]['msgLogId'], "authCode": "888888", "vaild": True}
        res = requests.post(url=api3, json=data3, headers=head, verify=False).json()
        code = res['data']
        return code

    def wr_excel(self, file_path, sheet, data):
        '''公共方法：读写excel,当data是列表时是读取对应单元格字段（字典则是修改单元格的值，如{'B4':"1"}）'''
        try:
            workbook = lw(filename=file_path)
            sheet = workbook[sheet]
            if type(data) is list:
                for i in data:
                    return sheet[i].value
            elif type(data) is dict:
                for key,value in data.items():
                    sheet[key] = value
                    workbook.save(filename=file_path)
            else:
                self.fail('data传参错误，请核对参数是否为列表或字典')
        except KeyError as e:
            raise KeyError(f'找不到sheet,请核对:{sheet}')
        except PermissionError as e:
            raise PermissionError('文件被占用，请关闭文件')
        except FileNotFoundError as e:
            raise FileNotFoundError(f'找不到文件,请核对：{file_path}')

    def get_org_info(self):
        '''公共方法：获取村集体信息'''
        api = '/api/admin/v1/smallProject/getDefaultInfo'
        res = self.post(api, data={}, account_type=0, phone=0)
        return res

    def project_type(self, data):
        '''公共方法：获取项目类别——需根据数据查找最后一级的项目类别编码'''
        level = [
                data['assetGroupCodeLevel5'],
                data['assetGroupCodeLevel4'],
                data['assetGroupCodeLevel3'],
                data['assetGroupCodeLevel2'],
                data['assetGroupCodeLevel1']
                ]
        for i in level:
            if i != '' and i is not None:
                break
        return i

    def project_type_name(self, data):
        '''公共方法：获取项目类别名称——需根据数据查找最后一级的项目类别名称'''
        level = [
                data['assetGroupLevel5Name'],
                data['assetGroupLevel4Name'],
                data['assetGroupLevel3Name'],
                data['assetGroupLevel2Name'],
                data['assetGroupLevel1Name']
                ]
        for i in level:
            if i != '' and i is not None:
                break
        return i

    def add_asset(self, asset_name):
        '''公共方法：资产登记'''
        res1 = self.get_org_info()['data']
        api = '/api/admin/v1/assetInfoResource/saveAssetResource'
        data = {
                "assetName": asset_name,
                "assetSelfCode": asset_name,
                "newAssetStatus": "1",  # 资产状态
                "provinceId": res1['areaOrganizationProvinceId'],
                "province": res1['areaOrganizationProvince'],
                "cityId": res1['organizationCityId'],
                "city": res1['areaOrganizationCity'],
                "areaId": res1['areaOrganizationAreaId'],
                "area": res1['areaOrganizationArea'],
                "streetId": res1['areaOrganizationStreetId'],
                "street": res1['areaOrganizationStreet'],
                "address": "详细地址",
                "purposeExplain": "用途说明",
                "landOccupation": self.randint,  # 占地面积
                "landOccupationUnit": 0,  # 占地面积单位
                "images": [
                "default/440105000000/202504/b411c705-54ce-472e-9900-34532db2fe27.jpg"
                ],
                "assetGroupCodeLevel1": "Z000000",
                "assetGroupLevel1Name": "资源性资产",
                "assetGroupCodeLevel2": "Z060000",
                "assetGroupLevel2Name": "林木",
                "assetGroupCodeLevel3": "Z060100",
                "assetGroupLevel3Name": "公益林",
                "assetSource": "自购",
                "landCertificateType": 1,#权证类别
                "ownership": "权属111",
                "commonAsset": False,#是否共有资产
                "commonAssetRate": 100,#资产占比
                "originVouchers": [],
                "sysOrganizationId": res1['organizationId'],
                }
        self.post(api, data, account_type=0, phone=0)

    def upload_file(self, file_list, file_type, upload_type=0):
        '''
        公共方法：
        上传文件并返回响应参数
        file_list是文件路径列表
        file_type上传文件类型（asset是资产附件，project立项附件，contract是合同附件）
        upload_type=1,则是调用上传身份证接口，用于上传合同时上传身份证正反面
        '''
        res_list = []
        base_url = self.env_config[self.env]['base_url']
        phone = self.env_config[self.env]['phones'][0]
        headers = {
            'sessionid': self.get_sessionid(phone, 0, 1),
            'Connection': 'keep-alive',
            'channel': 'admin'
        }
        # 获取文件名（去掉路径，只保留纯文件名）
        for file_path in file_list:
            file_name = os.path.basename(file_path)
            try:
                with open(file_path, 'rb') as f:
                    files = {'file': (file_name, f)}
                    url = base_url + '/api/file/v1/picture/uploadFileOnObs'
                    if upload_type==1:
                        url = base_url + '/api/file/v1/upload/uploadEncryptFileOnObs'
                    files.update({'folderName': (None, file_type)})
                    res = requests.post(
                        url=url,
                        files=files,
                        headers=headers,
                        verify=False
                    ).json()
                    res_list.append(res['data'])
            except IOError as e:
                self.fail(f'文件读取失败,请核对文件路径：{e}')
        return res_list

    def import_asset(self, asset_name):
        '''公共方法：资产导入'''
        print(asset_name)
        # 修改项目名称和资产编号
        self.wr_excel(self.import_asset_file, '资产分类模版', {'B4':asset_name,'C4':asset_name})
        # 导入资产模板
        base_url = self.env_config[self.env]['base_url']
        phone = self.env_config[self.env]['phones'][0]
        org_id = self.get_org_info()['data']['organizationId']
        headers = {
            'sessionid': self.get_sessionid(phone, 0, 0),
            'Connection': 'keep-alive',
            'channel': 'admin'
        }
        file_name = os.path.basename(self.import_asset_file)
        with open(self.import_asset_file, 'rb') as f:
            files = {'file': (file_name, f)}
            url = base_url + f'/api/admin/v1/asset/importAsset?execute=false&sysOrganizationId={org_id}'
            res = requests.post(
                url=url,
                files=files,
                headers=headers,
                verify=False
            ).json()
        self.assertEqual(res['message'], '操作成功')
        # 获取assetId
        api1 = '/api/admin/v1/asset/getAssetPageList'
        data1 = {"assetName": asset_name}
        res1 = self.post(api1, data1, account_type=0, phone=0)['data']['records']
        # 上传图片并返回图片路径列表，用于上传图片接口传参
        images = []
        file_list = self.upload_file(self.asset_photo, 'asset')
        for image in file_list:
            images.append(image['key'])
        # 上传图片接口
        api = '/api/admin/v1/asset/saveAssetImages'
        data = {
            "assetId": res1[0]['assetId'],
            "images": images
        }
        res = self.post(api, data, account_type=0, phone=0)

    def chooes_asset(self, asset_name):
        '''公共方法：选择资产，按资产名搜索去资产的信息用户项目关联资产'''
        api = '/api/admin/v1/asset/getChooseAssetList'
        data = {
            "assetName": asset_name,
            "sysOrganizationId": self.get_org_info()['data']['organizationId'],
            }
        res = self.post(api, data, 0, 0)['data']['records']
        return res

    def project_approval(self, project_name, tradeMode):
        '''公共方法：立项申请，tradeMode01公开竞价，02公开协商，03小额简易，04线下续约'''
        res1 = self.get_org_info()['data']
        res2 = self.chooes_asset(project_name)[0]
        if tradeMode == '01':
            api = '/api/admin/v1/assetProject/saveProjectApply'
        elif tradeMode == '02':
            api = '/api/admin/v1/openConsult/selectApply'
        elif tradeMode == '03':
            api = '/api/admin/v1/smallTrade/smallApply'
        elif tradeMode == '04':
            api = '/api/admin/v1/renewalTrade/renewalApply'
            # 按合同名查找合同信息
            api3 = '/api/admin/v1/assetProjectExternalContractHi/getPeriodPageList'
            data3 = {"contractName": project_name}
            res3 = self.post(api3, data3, 0, 0)['data']['records'][0]
            # 合同详情用于下方关联项目
            api2 = '/api/admin/v1/asset/getAssetBaseInfo'
            data2 = {
                "assetId": res3['assetId'],
                "assetProjectExternalContractHiId": res3['assetProjectExternalContractHiId']}
            res2 = self.post(api2, data2, 0, 0)['data']
        res2.update({"assetGroupCode": self.project_type(res2)})  # 需加上资产分类
        # 预计起始日期必须大于原合同截止日期，如果没有原合同或原合同日期小于当前时间，则需要大于当前时间
        contractEndDate = res2['contractEndDate']
        if contractEndDate is not None:
            contractEndDate = datetime.strptime(
                contractEndDate, "%Y-%m-%d %H:%M:%S")
        elif contractEndDate is None or contractEndDate < datetime.now():
            contractEndDate = datetime.now()
        projectStartDate = contractEndDate + \
            timedelta(days=self.randint)
        projectEndDate = projectStartDate + \
            timedelta(days=self.randint)
        projectStartDate = str(projectStartDate)[0:10]
        projectEndDate = str(projectEndDate)[0:10]
        # 计算出预计租赁期限
        api4 = '/api/admin/v1/dailyStatistic/open/calcContractDays'
        data4 = {"startDate": projectStartDate, "endDate": projectEndDate}
        res4 = self.post(api4, data4, 0, 0)['data']
        projectTradeYear = f'{res4["year"]}年{res4["month"]}月{res4["day"]}天'
        if res4["year"] == 0:
            projectTradeYear = projectTradeYear.replace('0年', '')
        if res4["month"] == 0:
            projectTradeYear = projectTradeYear.replace('0月', '')
        if res4["day"] == 0:
            projectTradeYear = projectTradeYear.replace('0天', '')
        data = {
            "tradeMode": tradeMode,
            "tradeType": choice(['01']),  # 交易类型1出租，2出让，3出售
            "tradeEarnestMoney": self.randint,  # 必填交易保证金
            "floorPrice": self.randint,  # 必填交易底价
            "minBidRange": 100,  # 必填最低加价
            "maxBidRange": 10000,  # 必填最高加价
            "assetDeliverDay": randint(1, 30),  # 必填多少日交付资产
            # 必填付款金额递增方式，False不递增，True递增
            "progressiveIncrease": choice([True, False]),
            # 递增方式 1=按比例递增 2=按固定金额递增
            "progressiveIncreaseWay": choice(['1', '2']),
            "progressiveIncreaseAmount": self.randint,  # 每次递增固定金额
            # 从第n个月开始递增
            "progressiveIncreaseStartMonth": randint(1, 30),
            "progressiveIncreaseMonth": randint(1, 30),  # 每n个月递增一次
            # 每次递增幅度为上期缴纳租金的n百分比
            "progressiveIncreaseIncrease": randint(1, 30),
            # 必填是否有免租期False无,True有，出让必为False
            "rentFree": choice([True, False]),
            "rentFreePeriod": randint(1, 30),  # 免租多少天，有免租期时必填
            "rentCollectMethod": choice([3]),  # 必填租金收取方式，0月，3年，4一次性
            # 是否永久出让 False=否 true=是
            "perpetualAssignment": choice([False]),
            "repostAssetProject": False,  # 必填是否自动挂牌False否，True是
            "requires": choice([True, False]),  # 必填是否需要证明材料小额不可选默认False
            "enrollType": 0,  # 必填允许报名类型，0全部，1自然人，2法人
            "blacklistEnter": False,  # 必填是否禁止警示名单报名，False允许，True禁止
            "payTradeEarnestMoney": True,  # 必填是否需要缴纳保证金，默认True是
            "projectStartDate": projectStartDate,
            "projectEndDate": projectEndDate,
            "projectTradeYear": projectTradeYear,
            # 预计交付日期，只有竞价、协商、小额出售模式才有，需大于原合同截止日期和预计租赁起始日期一样
            'predictDeliveryTime': projectStartDate,
            "isSubmit": 1,  # 是否提交，如果是临时保存则不需要传这个字段
            "priorityOriginalLessee": False,  # 必填，是否承租方优先权，默认False否,需达到特定条件
            "userName": None,  # 原承租方
            "userType": None,  # 原承租方类型1个人，2法人
            "idCardType": None,  # 原承租方证件类型
            "idCardNo": None,  # 原承租方证件号
            "agent": False,  # 是否代办
            "assetId": res2['assetId'],
            "organizationId": res1['organizationId'],
            "organizationName": res1['organizationName'],
            "provinceId": res1['organizationProvinceId'],
            "province": res1['organizationProvince'],
            "cityId": res1['organizationCityId'],
            "city": res1['organizationCity'],
            "areaId": res1['organizationAreaId'],
            "area": res1['organizationArea'],
            "streetId": res1['organizationStreetId'],
            "street": res1['organizationStreet'],
            "address": res1['organizationAddress'],
            "contact": res1['tenderUserName'],
            "phone": res1['tenderUserPhone'],
            "projectName": project_name,
            "projectType": self.project_type(res2),
            "projectTypeName": self.project_type_name(res2),
            "applyType": 1,  # 申请类型 1=公开协商-遴选申请 2=公开协商-项目申请
            "remarkIndustryRequire": "行业要求必填",
            "remarkFireControl": "消防情况说明必填",
            "detailParamList": [res2],
            "fileSaveParams": [
                {"fileType": 1,  # 民主表决书
                 "fileUrl": "project/440882000000/202401/0fb63d50-70e3-4ea1-b4ce-8e296b2aa0bc.png",
                 "fileName": "1ac77590368ff636d0b4f6a988133f55"
                 },
                {"fileType": 2,  # 合同样本
                 "fileUrl": "project/440882000000/202401/4f34a4da-480c-4bfc-ac21-2f6bb42005f0.doc",
                 "fileName": "1 - 副dsfgasgfa asdfas fasfasdfs asdf  afsf f sdaf 本"}],
            "remarkCondition": "承租人条件说明",
            "auditMaterialsSaveParams": [{"materialsName": "材料名称1需要证明材料时必填",
                                          "remark": "材料说明需要证明材料时必填"}]
        }
        if 1 == 1:
            if data['tradeType'] != '02':
                data.update({'perpetualAssignment': False})
            if data['tradeType'] == '03':
                data.update({'projectStartDate': None})
            if data['tradeType'] == '03' or data['perpetualAssignment']:
                data.update({'projectEndDate': None})
                data.update({'projectTradeYear': None})
            if tradeMode == '04':
                data.update({'tradeType': choice(['01', '02'])})
                data.update({'originalProjectNo': res3['contractNo']})
                data.update({'tradeEarnestMoney': None})
                data.update({'payTradeEarnestMoney': False})
                data.update({'enrollType': None})
                data.update({'blacklistEnter': None})
            if tradeMode == '04' and data['tradeType'] == '03':
                data.update({'floorPrice': None})
                data.update({'assetDeliverDay': None})
            if tradeMode != '01':
                data.update({'minBidRange': None})
                data.update({'maxBidRange': None})
                data.update({'maxBidRange': None})
                data.update({'repostAssetProject': None})
            if data['tradeType'] != '01':
                data.update({'progressiveIncrease': False})
                data.update({'rentFree': False})
            if not data['progressiveIncrease']:
                data.update({'progressiveIncreaseWay': None})
            if not data['progressiveIncrease']:
                data.update({'progressiveIncreaseAmount': None})
                data.update({'progressiveIncreaseMonth': None})
            if data['progressiveIncreaseWay'] != 1:
                data.update({'progressiveIncreaseIncrease': None})
            if data['progressiveIncreaseWay'] != 2:
                data.update({'progressiveIncreaseAmount': None})
            if not data['rentFree']:
                data.update({'rentFreePeriod': None})
            if tradeMode != '01' and tradeMode != '02':
                data.update({'requires': False})
            if data['tradeType'] != '03' or tradeMode == '04':
                data.update({"predictDeliveryTime": None})
            if tradeMode != '02':
                data.update({'applyType': None})
        self.post(api, data, 0, 0)

    def project_audit(self, project_name, audit_types=[1], status=1):
        '''
        公共方法：
        立项审核/遴选公告审核
        status: 1是通过，0是驳回
        audit_type: [1]是一审，[2]为二审，[3]为三审,[1,2,3]连续123审,['02']是遴选公告审核
        '''
        for audit_type in audit_types:
            api1 = '/api/admin/v1/activiti/getActivitiPage'
            if audit_type == '02':
                data1 = {
                    "projectName": project_name,
                    "tradeMode": "02",
                    "projectStatusList": [101]
                }
            else:
                data1 = {
                    "projectName": project_name,
                    "projectStatusList": [int(audit_type) + 10],
                    'sortField': 1
                }
            audit_project_info = self.post(api1, data1, 1, 1)['data']['records']
            self.assertNotEqual(audit_project_info, [], f'{audit_type}审列表搜查不到该项目:请核对项目名称')
            api = '/api/admin/v1/activiti/activitiInstance'
            data = {
                "assetProjectId": audit_project_info[0]['assetProjectId'],
                "taskId": audit_project_info[0]['taskId'],
                "businessKey": audit_project_info[0]['businessKey'],
                "status": status,
                "annotation": "审核意见1"
            }
            self.post(api, data, 1, 1)

    def get_period(self, year, month, day, mode):
        '''公共方法：根据租赁期限和租赁方式获取分期数,mode=0是按月，3是按年'''
        period = 0
        if mode == 0:
            period = year * 12 if year not in (0, None) else 0
            period += month if month not in (0, None) else 0
            period += 1 if day not in (0, None) else 0
        elif mode == 3:
            period = year if year not in (0, None) else 0
            period += 1 if month not in (0, None) or day not in (0, None) else 0
        return period

    def uoload_contract_files(self):
        '''公共方法：上传合同文件'''
        dict = {}
        files = []
        files_list = self.upload_file(self.contract_file, 'contract')
        for file in files_list:
            dict.update({
                "fileName": file['fileName'],
                "fileUrl": file['key']
            })
            files.append(dict)
        return files

    def upload_contract(self, project_name, timeout=120, sleep=30):
        '''
        公共方法：
        上传合同，seconds是在超长时间，sleep是每多少秒查找一遍
        出售和出让都没有分期，只有出租才有分期
        '''
        now = str(datetime.now())[:10]
        timeout = datetime.now() + timedelta(seconds=timeout)
        # 循环按项目名找待上传合同项目，当找到项目或时间超长跳出循环
        while True:
            api1 = '/api/admin/v1/assetProjectContract/getContractAudit'
            data1 = {
                "projectName": project_name,
                "type": 4
            }
            res1 = self.post(api1, data1, 0, 0)['data']['records']
            if res1 != [] or datetime.now() > timeout:
                self.assertTrue(res1, f'待上传合同列表没有该项目:{project_name}')
                break
            print(f'上传合同列表没有该项目可能状态未流转{sleep}秒后会重试，超长时间{timeout}秒')
            time.sleep(sleep)
        #获取项目信息
        api2 = '/api/admin/v1/assetProject/getProjectDetail'
        data2 = {"assetProjectId": res1[0]['assetProjectId']}
        res2 = self.post(api2, data2, 0, 0)['data']
        #获取合同信息
        api3 = '/api/admin/v1/assetProjectContract/getContractDetail'
        data3 = {
            "assetProjectId": res1[0]['assetProjectId'],
            "tradeNo": res1[0]['tradeNo']
            }
        res3 = self.post(api3, data3, 0, 0)
        # 有旧合同则生成大于旧截止日期的合同开始日期/支付日期，没有或合同已结束则开始日期/支付日期取当前日期
        year = res3['data']['contractYear'] if res3['data']['contractYear']!=None else 0
        month = res3['data']['contractMonth'] if res3['data']['contractMonth']!=None else 0
        day = res3['data']['contractDay'] if res3['data']['contractDay']!=None else 0
        old_contract_end = res2['oldContractEndDate']
        if old_contract_end:
            old_contract_end = datetime.strptime(old_contract_end, "%Y-%m-%d %H:%M:%S")
        if old_contract_end is None or old_contract_end < datetime.now():
            old_contract_end = datetime.now()
            first_paydate = now
        else:
            first_paydate = str(old_contract_end + timedelta(days=self.randint))[:10]
        contract_start = old_contract_end + timedelta(days=self.randint)
        contract_end = contract_start + relativedelta(years=year,months=month,days=day-1)
        contract_start = str(contract_start)
        contract_end = str(contract_end)
        # 根据合同分期数生成分期数据列表
        mode = res2['assetProject']['rentCollectMethod']
        period = self.get_period(year, month, day, mode)
        period_list = [] #分期信息列表
        total_amount = 0 #合同总价
        for i in range(period):
            amount = self.randint
            period_dict = {'period': i+1,"payEndDate": now,"periodMoney": amount}
            period_list.append(period_dict)
            total_amount += amount
        idcard_file = self.upload_file(self.idcard_files,'contract',1)[0]['key']
        api = '/api/admin/v1/assetProjectContract/saveAssetProjectContract'
        # api = '/api/admin/v1/assetProjectContract/temporarySaveAssetProjectContract'
        data = {
            "type": 1,#类别 0-合同上传，1-合同异常处理
            "assetProjectId": res1[0]['assetProjectId'],
            "tradeNo": res1[0]['tradeNo'],
            "contractNo": PublicCqjy.now_str,#村集体合同自编号
            "contractStartDate": contract_start,  # 合同开始时间必须大于原合同时间
            "contractYear": year,
            "contractMonth": month,
            "contractDay": day,
            "contractEndDate": contract_end,  # 合同结束
            "payDateUnit": res2['projectScheme']['floorPriceUnit'],
            "idCardBackFile": idcard_file,
            "idCardFrontFile": idcard_file,
            "businessLicenseFile": self.upload_file(self.asset_photo,'contract')[0]['key'],#营业执照
            "files": self.uoload_contract_files(),#合同附件
            "contractInstallmentParam": {
                "firstPayDate": now, #分期首期履约日期
                "periodTotal": period, #分期数
                "periodMode": 2, #分期方式 1=固定周期 2 =自定义
                "periodMoneyType": 2, #每期金额类型 1=固定金额 2=自定义金额 3=金额递增
            },
            "contractInstallmentDetailParamList": period_list,
            "clinchAmount": total_amount,#本方金额
            "reinstallment": False,#是否重新分期 否=false 是=true
            "contractSignDate": now,#签订日期
            "contractSignPlace": "签订地点:广州",
            "tradeType": "01",#交易类别
            "ourOwnProportion": 100,#本方占比
            "totalPrice": total_amount,#合同总价
            "firstPayDate": first_paydate,# 支付日期没有分期的才需求这个参数,必须大于原合同截止
            "paymentType": 3,
            "contractName": project_name
        }
        #出售类型没有分期信息、合同起始、合同终止、合同期限
        if res2['assetProject']['tradeType'] == '03':  #出售合同不需要传的参数
            del data['contractStartDate'], data['contractEndDate'], data['contractMonth'], data['contractDay'], data['contractYear'],data['contractInstallmentParam'],data['contractInstallmentDetailParamList']
        #出让类型不需要分期
        elif res2['assetProject']['tradeType'] == '02':
            del data['contractInstallmentParam'],data['contractInstallmentDetailParamList']
        # 出租类型分期里有首期支付日期不需要再传支付日期,且需要分期所以reinstallment=True
        else:
            del data['firstPayDate']
            data.update({'reinstallment':True})
        self.post(api, data, 0, 0)

    def contract_audit(self, project_name, types=[1], status=1):
        '''公共方法：合同审核，type1是一审，2是二审，3是三审，status1是通过，0是驳回'''
        for type in types:
            api1 = '/api/admin/v1/assetProjectContract/getContractAudit'
            data1 = {"projectName": project_name, "type": type}
            res1 = self.post(api1, data1, 1, 1)['data']['records']
            self.assertNotEqual(res1, [], f'合同{type}审列表查不到该项目：{project_name}')
            act = {
                1: 'contractFirstInstance',
                2: 'contractSecondInstance',
                3: 'contractThirdInstance'
            }
            api = '/api/admin/v1/activiti/activitiInstance'
            data = {
                "status": status,
                "annotation": "通过",
                "actNode": act[type],
                "businessKey": res1[0]['businessKey'],# 工作流节点
                "assetProjectId": res1[0]['assetProjectId'],
            }
            self.post(api, data, 1, 1)

    def run_sql(self, sql, database='cqjy'):
        '''公共方法：封装执行数据库语句，database是数据库名'''
        # 判断是uat还是测试环境，连接不同的数据库
        if self.env == 'test':
            mysql = pymysql.connect(
                host='39.108.128.154',
                user='b2bwings',
                password='b2bwings666!',
                port=3307,
                database=database)
        elif self.env == 'uat':
            mysql = pymysql.connect(
                host='47.107.176.206',
                user='root',
                password='test666!',
                port=3307,
                database=database)
        cursor = mysql.cursor()
        try:
            cursor.execute(sql)
            mysql.commit()
            if cursor.description:  # 获取数据库字段名称及其他信息，None的话则不是查询语句所以不用执行下面操作
                keys = [column[0]
                        for column in cursor.description]  # 从所有信息中获取字段名称
                values = cursor.fetchall()  # 按照顺序获取到执行sql语句后的数据库值
                if values == ():
                    return None
                else:
                    dict1 = dict(zip(keys, values[0]))  # 以字典的形式返回查询数据的第一条数据
                    return dict1
        except Exception as e:
            mysql.rollback()
            self.assertIsNone(e)
        cursor.close()
        mysql.close()

    def create_sub_account(self, asset_name, type):
        '''公共方法：生成子账号,type=0是产权项目,1是小额工程'''
        if self.env != 'pro':
            if type == 0:
                data1 = self.run_sql(
                    f"select * from t_asset_project where project_name='{asset_name}'ORDER BY gmt_create DESC")
                trade_no = data1['trade_no']
            elif type == 1:
                data1 = self.run_sql(
                    f"SELECT * FROM `t_small_project` where project_name='{asset_name}'ORDER BY gmt_create DESC")
                trade_no = data1['project_trade_no']
            sub_account = self.run_sql(
                f"SELECT * FROM `t_sub_account` WHERE project_trade_no='{trade_no}'",
                "cqjy-account")
            if sub_account is None:  # 判断该项目是否已经存在子账号，没有才有向数据库插入子账号
                self.run_sql(
                    f"INSERT INTO `cqjy-account`.`t_sub_account`(`sub_account_id`, `app_id`, `app_name`, `sub_app_id`, `sub_account_no`, `sub_account_name`, `sub_req_no`, `organization_name`, `organization_id`, `organization_code`, `main_account_no`, `account_bank_code`, `main_account_name`, `open_bank_name`, `stop_in_time`, `project_trade_no`, `auth_code`, `cancel_reason`, `account_status`, `project_name`, `gmt_create`, `gmt_modified`, `create_user_id`, `modified_user_id`) VALUES ({randint(1560000000000000000, 1569999999999999999)}, 'gdnccqjy', '广东省农村产权流转交易管理服务平台', '100000', '{randint(9558800000000000000, 9558899999999999999)}', '八赏冒乐蹬饥符曾绍亥猴睹崎等佑', '{randint(1500000000000000000, 1700000000000000000)}', '{data1['trade_organization_name']}', {data1['trade_organization_id']}, NULL, '3602023929200100926', '102', '八赏冒乐蹬饥符曾绍亥猴睹崎等佑', '开户行支行名', '', '{trade_no}', '{datetime.now().strftime('%Y%m%d%H%M%S')}', NULL, '00', '{asset_name}', '{str(datetime.now())[0:19]}', '{str(datetime.now())[0:19]}', 0, NULL);")
            else:
                print("该项目已经生成子账号，请勿重复生成！")

    def enroll(self, project_name, mode, phone, account_type):
        '''公共方法：报名,mode'03'小额简易报名，'01'公开竞价报名，'02'公开协商遴选申请,phone,acconut_type是报名用户账号和类型'''
        if mode == '03':
            api1 = '/api/auction/v1/smallTrade/open/getSmallTradePageList'
        elif mode == '01':
            api1 = '/api/auction/v1/auctions/open/getAuctionPageList'
        elif mode == '02':
            api1 = '/api/auction/v1/openConsult/open/getConsultTradePageList'
        data1 = {"projectName": project_name, "tradeMode": mode}
        res1 = self.post(api1, data1, phone, account_type)['data']['records']
        # 判断是否需要上传审核材料
        api2 = '/api/auction/v1/assetProjectAuditMaterials/getAssetProjectAuditMaterials'
        data2 = {"assetProjectId": res1[0]['assetProjectId']}
        res2 = self.post(api2, data2, phone, account_type)
        if res2['data'] == {}:
            print('该项目不需要上传证明材料')
            data = {"assetProjectId": res1[0]
                    ['assetProjectId'], "files": []}
        else:
            print('该项目需要上传证明材料')
            data = {"assetProjectId": res2['data'][0]['assetProjectId'], "files": [
                {"assetProjectAuditMaterialsId": res2['data'][0]['assetProjectAuditMaterialsId'],
                 "fileUrl": "cqjy/000000/202405/103b21b5-1ad3-4352-beda-534cca62727d.png"}]}
        api = '/api/auction/v1/assetProjectEnroll/saveAssetProjectEnroll'
        self.post(api, data,  phone, account_type)
        self.paymoney_sql(project_name)

    def paymoney_sql(self, asset_name, type=0):
        '''公共方法：缴纳保证金——type0竞价、协商、简易，type1小额工程'''
        if type == 0:
            pay_earnest_money = self.run_sql(
                f"select trade_earnest_money from t_asset_project a join t_asset_project_scheme b on a.asset_project_id=b.asset_project_id where a.project_name='{asset_name}'")['trade_earnest_money']
            sql = f'UPDATE t_asset_project_enroll a join t_asset_project b on a.asset_project_id=b.asset_project_id SET system_review_status=1,pay_earnest_money_date="{str(datetime.now())[0:19]}",pay_earnest_money={pay_earnest_money} WHERE project_name="{asset_name}"'
        else:
            pay_earnest_money = self.run_sql(f"SELECT trade_earnest_money FROM `t_small_project` WHERE project_name='{asset_name}'")[
                'trade_earnest_money']  # 获取保证金金额
            sql = f'UPDATE t_asset_project_enroll a join t_small_project b on a.small_project_id=b.small_project_id SET system_review_status=1,pay_earnest_money_date="{str(datetime.now())[0:19]}",pay_earnest_money={pay_earnest_money} WHERE project_name="{asset_name}"'
        self.run_sql(sql)

    def cloud_vote(self, project_name, phone, result=1):
        '''公共方法：村民表决,result0不同意 1同意 2弃权,phone是用户账号'''
        api1 = '/api/admin/v1/vote/getPreVoteProjectPage'
        data1 = {"projectName": project_name}
        res1 = self.post(api1, data1, phone, 4)["data"]["records"]
        self.assertTrue(res1, f'表决中列表没有该项目{project_name}')
        api = '/api/admin/v1/vote/saveVoteInfo'
        data = {"cloudVoteResult": result,
                "assetProjectCloudId": res1[0]['assetProjectCloudId']}
        self.post(api, data, phone, 4)

    def modify_cloud_task(self, project_name):
        '''公共方法：修改云表决定时任务'''
        end_date = str(datetime.now() + timedelta(seconds=5))
        sql = f"UPDATE t_asset_project a join t_asset_project_cloud_timer_job b on a.asset_project_id=b.asset_project_id set b.end_date='{end_date}' where a.project_name='{project_name}'"
        self.run_sql(sql)

    def vote_change_project(self, project_name):
        '''公共方法：表决转立项,在固定时间内循环搜索项目并转立项'''
        timeout = datetime.now() + timedelta(seconds=90)
        while True:
            api1 = '/api/admin/v1/assetProjectCloud/getCloudVotedList'
            data1 = {"assetName": project_name, "voteStatus": 4}
            res1 = self.post(api1, data1, 0 ,0)['data']['records']
            if res1 == [] and datetime.now() >= timeout:
                self.assertTrue(res1, f'表决转立项列表没有该项目：{project_name}')
            elif res1:
                break
            time.sleep(10)
        file_info = self.upload_file(self.vote_change_project_file,file_type='project')[0]
        api = '/api/admin/v1/assetProjectCloud/submit'
        data ={
                "assetProjectId": res1[0]['assetProjectId'],
                "assetProjectCloudId": res1[0]['assetProjectCloudId'],
                "contractFiles": [
                {
                "fileName": file_info['fileName'],
                "fileUrl": file_info['key']
                }],
                "operateType": 3
                }
        res = self.post(api, data, 0 ,0)

    def initate_cloud_vote(self, projectName, tradeMode, voteType="01"):
        '''云表决公共方法：——发起云表决，tradeMode,#交易模式 01=公开竞价 03=小额简易交易 02公开协商'''
        res1 = self.get_org_info()['data']
        assetdetail = self.chooes_asset(asset_name=projectName)[0]
        cloudDetailBase4MostVo = {"assetGroupCode": self.project_type(assetdetail),
                                  "tradeTypeName": "出租",
                                  "project_type_name": self.project_type_name(assetdetail),
                                  "voteType": voteType,  # 表决类型 0=立项表决 1=遴选表决 2=拟定意向人表决
                                  "tradeMode": tradeMode,  # 交易模式 01=公开竞价 02=公开协商 03=小额简易交易 04=续约交易
                                  "tradeType": "01",  # 交易类型/交易类别 01=出租 02=出让 99=其它
                                  "organizationId": res1['organizationId'],
                                  "organizationName": res1['organizationName'],
                                  "contact": res1['tenderUserName'],
                                  "phone": res1['tenderUserPhone'],
                                  "projectName": projectName,
                                  "projectType": self.project_type(assetdetail),
                                  "projectTypeName": self.project_type_name(assetdetail)
                                  }
        cloudDetailBase4MostVo.update(assetdetail)
        data = {"cloudDetailBase4MostVo": cloudDetailBase4MostVo,
                "cloudDetailIntenderDemandVo": {
                    "intenderDemand": 0,  # 是否需要资质证明材料 0=否 1=是
                    "remarkCondition": "受让人条件说明",
                    "materials": [{"materialsName": "材料名", "remark": "材料说明"}],
                    "enrollType": 0,  # 允许报名的用户类型 0=全部 1=自然人 2=法人
                    "blacklistEnter": 1  # 是否禁止警示名单报名 0=否 1=是
                },
                "cloudDetailOtherQueryVo": {
                    "remark": None,
                    "remarkFireControl": "消防",
                    "remarkIndustryRequire": "行业要求",
                    "remarkOther": None,
                    "remarkOtherClause": None,
                    "remarkTaxation": None},
                "cloudDetailSchemeQueryVo": {
                    "tradeEarnestMoneyStr": "壹元整",
                    "priorityOriginalLessee": 0,  # 是否原承租方优先权
                    "payTradeEarnestMoney": 1,  # 必填是否需要缴纳保证金，默认1,0否
                    "tradeEarnestMoney": 1,  # 交易保证金
                    "floorPrice": 123,  # 必填交易底价
                    "minBidRange": 100,  # 最低加价
                    "maxBidRange": 200,  # 最高加价
                    "assetDeliverDay": 23,  # 非必填多少日交付资产
                    "progressiveIncrease": None,  # 是否递增付款金额 0=否 1=是
                    "progressiveIncreaseWay": None,  # 递增方式 1=按比例递增 2=按固定金额递增
                    "progressiveIncreaseAmount": None,  # 每次递增固定金额
                    "progressiveIncreaseStartMonth": None,  # 从第n个月开始递增
                    "progressiveIncreaseMonth": None,  # 每n个月递增一次
                    "progressiveIncreaseIncrease": None,  # 每次递增幅度为上期缴纳租金的n百分比
                    "rentFree": 0,  # 必填是否有免租期0无,1有，出让必为False
                    "rentFreePeriod": None,  # 免租多少天，有免租期时必填
                    "floorPriceUnit": 0,  # 租金收取方式 0=月 1=季 2=半年 3=年 4=一次性
                    "projectStartDate": str(datetime.now())[0:10],  # 必填租赁起始时间
                    # 必填租赁截止时间
                    "projectEndDate": str(datetime.now() + timedelta(days=10))[0:10],
                    "projectTradeYear": "10天",
                    "perpetualAssignment": False,
                    # 必填是否永久出让，默认False否，只有出让才有True，True时projectEndDate为None,projectTradeYear为“永久出让”
                    "autoListing": 0,  # 流拍后自动重新挂牌 True=自动重新挂牌 False=不自动挂牌
                    "contractExpirationDate": 10},  # 合同截止日期天数 遴选公告、续约申请、续约申请（线下合同）三个交易方案页新增合同签订截止日期
                "cloudVotedRule": {
                    "voteUser": "000",  # 成员类型 000=所有成员 111=成员代表和户代表 110=仅成员代表 101=仅户代表
                    "voteContinueDays": 5,  # 发起表决次日多少个工作日内表决
                    "assetProjectCloudId": ""},
                "contractFiles": [{"fileName": "1合同",
                                   "fileUrl": "project/440882000000/202308/b5905d18-7f8b-44d8-9f25-72636fc0b259.doc"}],
                "otherFiles": [{"fileName": "1 - 副dsfgasgfa asdfas fasfasdfs asdf  afsf f sdaf 本",
                                "fileUrl": "project/440882000000/202308/5a1a14ea-99ab-4fb4-b123-d23d140bfaa5.doc"}],
                "operateType": 1}  # 操作类型 1=提交 2=暂存 3=发布 4=重新发起提交
        api = '/api/admin/v1/assetProjectCloud/cloudTS4Most'
        self.post(api, data, 0, 0)

    def contract_audit_gc(self, project_name, types=[1], status=1):
        '''小额工程——合同审核,type1是一审，2是二审'''
        for type in types:
            # 按项目名搜索待合同二审项目
            if type == 1:
                api1 = '/api/admin/v1/smallProjectContractAudit/getContractFirstAuditPageList'
                auditKey = 'contractFirstInstance'
            elif type == 2:
                api1 = '/api/admin/v1/smallProjectContractAudit/getContractSecondAuditPageList'
                auditKey = 'contractSecondInstance'
            data1 = {"projectName": project_name}
            res1 = self.post(api1, data1, 1, 1)['data']['records']
            # 审核
            api = '/api/admin/v1/smallProjectContractAudit/audit'
            data = {
                "smallProjectId": res1[0]['smallProjectId'],
                "status": status,
                "annotation": "通过",
                "auditKey": auditKey}
            self.post(api, data, 1, 1)

    def upload_contract_gc(self, project_name):
        '''小额工程——上传合同'''
        # 根据项目名称查找待上传合同项目
        api1 = '/api/admin/v1/smallProjectContract/getContractPageList'
        data1 = {"projectName": project_name}
        res1 = self.post(api1, data1, 0, 0)['data']['records']
        api = '/api/admin/v1/smallProjectContract/contractUpload'
        data = {"contractStartDate": str(datetime.now())[0:10],
                "contractEndDate": str(datetime.now() + timedelta(days=20))[0:10],
                "contractPeriod": "0年0月20天",
                "smallProjectId": res1[0]['smallProjectId'],
                "fileList": [{"fileName": "part-00637-2927.jpg",
                              "fileUrl": "https://cqjy-test.b2bwings.com/obs/default/440882000000/202304/5252b837-b698-40b4-a5ab-327dd1e71372.jpg"}]}
        self.post(api, data, 0, 0)

    def bid_agree(self, project_name, phone):
        '''小额工程——中标人同意'''
        # 根据项目名搜索,获取smallProjectId
        api1 = '/api/auction/v1/smallProject/open/hall/queryByParam'
        data1 = {"projectName": project_name, "tradeMode": "05"}
        res1 = self.post(api1, data1, phone, 3)['data']['records']
        api = '/api/auction/v1/smallProject/winConfirm'
        data = {"smallProjectId": res1[0]['smallProjectId'], "status": 1}
        self.post(api, data, phone, 3)

    def bid_winner(self, project_name, phone):
        '''小额工程——指定中标方'''
        timeout = datetime.now() + timedelta(seconds=480)  # 超长时间
        while True:
            # 循环根据项目名查找指定中标项目，当找到项目或时间超长跳出循环
            api1 = '/api/admin/v1/smallProjectWin/getWinPageList'
            data1 = {"projectName": project_name}
            res1 = self.post(api1, data1, 0, 0)['data']['records']
            now = datetime.now()
            if res1 == [] and now > timeout:
                self.assertTrue(res1, f'指定中标列表没有该项目：{api1}')
            elif res1 != []:
                break
            print('等待项目流程到待指定中标方')
            time.sleep(20)
        # 查找投标方名称，获取assetProjectEnrollId
        api2 = '/api/admin/v1/smallProjectWin/getEnrollPageList'
        data2 = {"smallProjectId": res1[0]['smallProjectId']}
        res2 = self.post(api2, data2, 0, 0)['data']['records']
        for i in res2:
            if i['contactPhone'] == str(phone):
                res2 = i
        # 根据assetProjectEnrollId查找投标人信息
        api3 = '/api/admin/v1/smallProjectWin/getEnrollDetail'
        data3 = {"enrollId": res2['assetProjectEnrollId']}
        res3 = self.post(api3, data3, 0, 0)
        # 指定中标人
        api = '/api/admin/v1/smallProjectWin/decideWin'
        data = {"winReason": "中标原因",
                "winContact": res3['data']['contactPhone'],  # 联系人手机
                "winContactUser": res3['data']['legalRepresent'],  # 联系人
                "winId": res3['data']['bidUserId'],  # bidUserId
                "winIdCard": res3['data']['legalPersonCode'],  # 证件号
                "winIdCardType": res3['data']['legalPersonCodeType'],  # 证件类型
                "winName": res3['data']['legalPerson'],  # 投标人名称
                "smallProjectId": res1[0]['smallProjectId'],
                "winMoney": f"{randint(0,10000)}",  # 中标金额
                "fileList": [{"fileName": "part-00637-2927.jpg",
                              "fileUrl": "default/440882000000/202304/9c2d6158-0008-4f89-8700-4e3d670fb5b2.jpg"}]}
        self.post(api, data, 0, 0)

    def enroll_gc(self, project_name, phone, account_type):
        '''小额工程——法人报名，teles报名人登陆的手机号码列表可以多个账号一起报名'''
        # 根据项目名搜索,获取smallProjectId
        api1 = '/api/auction/v1/smallProject/open/hall/queryByParam'
        data1 = {"projectName": project_name, "tradeMode": "05"}
        res1 = self.post(api1, data1, phone, account_type)['data']['records']
        # 法人账号进行报名
        api = '/api/auction/v1/smallProject/smallProjectEnroll'
        data = {"smallProjectId": res1[0]['smallProjectId']}
        self.post(api, data, phone, account_type)

    def publish(self, project_name, enrollEndDate, tenderEndDate):
        '''小额工程——发布交易公告,enrollEndDate报名剩余时间，tenderEndDate投标截止时间'''
        # 按项目名查找待发布的项目详情
        api1 = '/api/admin/v1/smallProjectTrade/getTradePageList'
        data1 = {"projectName": project_name}
        res1 = self.post(api1, data1, 1, 1)['data']['records']
        # 提交发布公告
        api = '/api/admin/v1/smallProjectTrade/publicTrade'
        data = {"smallProjectId": res1[0]['smallProjectId'],
                "publicStartDate": str(datetime.now())[0:10],  # 公示期
                "publicEndDate": str(datetime.now() + timedelta(days=10))[0:10],
                # 报名截止时间
                "enrollEndDate": str(datetime.now() + timedelta(seconds=enrollEndDate))[0:19],
                # 投标截止时间
                "tenderEndDate": str(datetime.now() + timedelta(seconds=tenderEndDate))[0:19],
                # 开标签到开始
                "auctionSignStartDate": str(datetime.now() + timedelta(seconds=120))[0:19],
                # 开标签到截止
                "auctionSignEndDate": str(datetime.now() + timedelta(seconds=180))[0:19],
                # 开标开始时间
                "auctionStartDate": str(datetime.now() + timedelta(seconds=240))[0:19],
                # 评标签到开始
                "evaluateSignStartDate": str(datetime.now() + timedelta(seconds=300))[0:19],
                # 评标签到截止
                "evaluateSignEndDate": str(datetime.now() + timedelta(seconds=360))[0:19],
                # 评标开始
                "evaluateStartDate": str(datetime.now() + timedelta(seconds=420))[0:19],
                #合同签订截止日期
                "contractSignEndPeriod":10,
                "winPublicPeriod": 5 # 中标候选人公示周期
                }
        self.post(api, data, 1, 1)

    def project_approval_gc(self, project_name):
        '''小额工程——立项申请'''
        print(project_name)
        res1 = self.get_org_info()
        api = '/api/admin/v1/smallProject/saveProjectApply'
        data = {"projectName": project_name,
                "organizationName": res1['data']['organizationName'],
                "organizationId": res1['data']['organizationId'],
                "organizationProvince": res1['data']['organizationProvince'],
                "organizationProvinceCode": res1['data']['organizationProvinceCode'],
                "organizationCity": res1['data']['organizationCity'],
                "organizationCityCode": res1['data']['organizationCityCode'],
                "organizationArea": res1['data']['organizationArea'],
                "organizationAreaCode": res1['data']['organizationAreaCode'],
                "organizationStreet": res1['data']['organizationStreet'],
                "organizationStreetCode": res1['data']['organizationStreetCode'],
                "organizationAddress": res1['data']['organizationAddress'],
                "areaOrganizationName": res1['data']['areaOrganizationName'],
                "areaOrganizationId": res1['data']['areaOrganizationId'],
                "areaOrganizationCode": res1['data']['areaOrganizationCode'],
                "areaOrganizationProvince": res1['data']['areaOrganizationProvince'],
                "areaOrganizationProvinceCode": res1['data']['areaOrganizationProvinceCode'],
                "areaOrganizationProvinceId": res1['data']['areaOrganizationProvinceId'],
                "areaOrganizationCity": res1['data']['areaOrganizationCity'],
                "areaOrganizationCityCode": res1['data']['areaOrganizationCityCode'],
                "areaOrganizationCityId": res1['data']['areaOrganizationCityId'],
                "areaOrganizationArea": res1['data']['areaOrganizationArea'],
                "areaOrganizationAreaCode": res1['data']['areaOrganizationAreaCode'],
                "areaOrganizationAreaId": res1['data']['areaOrganizationAreaId'],
                "areaOrganizationStreet": res1['data']['areaOrganizationStreet'],
                "areaOrganizationStreetCode": res1['data']['areaOrganizationStreetCode'],
                "areaOrganizationStreetId": res1['data']['areaOrganizationStreetId'],
                "areaOrganizationAddress": res1['data']['areaOrganizationAddress'],
                "tenderUserName": res1['data']['tenderUserName'],
                "tenderUserPhone": res1['data']['tenderUserPhone'],
                "partyAHead": "甲方负责人1",
                "partyAPhone": "15000000000",
                "projectProvinceId": res1['data']['areaOrganizationProvinceId'],
                "projectProvince": res1['data']['organizationProvince'],
                "projectCityId": res1['data']['areaOrganizationCityId'],
                "projectCity": res1['data']['areaOrganizationCity'],
                "projectAreaId": res1['data']['areaOrganizationAreaId'],
                "projectArea": res1['data']['areaOrganizationArea'],
                "projectStreetId": res1['data']['areaOrganizationStreetId'],
                "projectStreet": res1['data']['areaOrganizationStreet'],
                "projectDetailAddress": res1['data']['areaOrganizationAddress'],
                "areaOrganizationUser": res1['data']['tenderUserName'],
                "areaOrganizationPhone": res1['data']['tenderUserPhone'],
                "tenderTypeName": "设计",
                "tenderTypeCode": "02",
                "projectTypeName": "房屋建筑工程",
                "projectTypeCode": "01",
                "projectIndustryCategoryCode": "D000000000",
                "projectIndustryCategoryName": "电力、热力、燃气及水生产和供应业",
                "tenderMethod": "01",
                "projectContent": "建设内容及规模",
                "tradeEarnestMoney": 1,  # 保证金
                "standardPeriod": 10,  # 标准工期
                "tenderPeriod": 10,  # 招标工期
                "evaluateMethod": "01",  # 评标方法01是综合评分，02是摇珠
                "tenderPrice": 10,  # 招标控制价
                "blacklistEnter": "2",  # 是否禁止警示名单参与1是2否
                "remark": '其他',
                "moneySource": "资金来源",
                "organizationCode": res1['data']['organizationCode'],
                "fileList": [],
                "projectRegionCode": res1['data']['areaOrganizationStreetCode']# 最后一级区域编码
                }
        self.post(api, data, 0, 0)

    def project_audit_gc(self, project_name, types=[1], status=1):
        '''小额工程——立项审核,type1是一审，2是二审，3是三审'''
        for type in types:
            # 按项目名搜索待审核项目
            if type == 1:
                api1 = '/api/admin/v1/smallProjectAudit/getFirstAuditPageList'
                auditKey = 'activitiFirstInstance'
            elif type == 2:
                api1 = '/api/admin/v1/smallProjectAudit/getSecondAuditPageList'
                auditKey = 'activitiSecondInstance'
            elif type == 3:
                api1 = '/api/admin/v1/smallProjectAudit/getThirdAuditPageList'
                auditKey = 'activitiThirdInstance'
            data1 = {"projectName": project_name}
            res1 = self.post(api1, data1, 1, 1)['data']['records']
            api = '/api/admin/v1/smallProjectAudit/audit'
            data = {
                "smallProjectId": res1[0]['smallProjectId'],
                "status": status,
                "annotation": "通过",
                "auditKey": auditKey}
            res = self.post(api, data, 1, 1)

    def agrees_intention(self, project_name, phone, account_type):
        '''公开协商——意向人同意,status1是同意，0是不同意'''
        # 按项目搜索项目
        api1 = '/api/auction/v1/openConsult/open/getConsultTradePageList'
        data1 = {"projectName": project_name, "tradeMode": "02"}
        res1 = self.post(api1, data1, phone, account_type)['data']['records']
        api = '/api/auction/v1/openConsult/intentConfirm'
        data = {
            "assetProjectId": res1[0]['assetProjectId'],
            "businessKey": res1[0]['businessKey'],

            "status": 1}
        self.post(api, data, phone, account_type)

    def confirm_intention(self, project_name, phone):
        '''公开协商——指定意向人并转立项申请,phone是指定意向人手机号'''
        # 搜索待指定意向人项目,等待是否置灰按钮意向人按钮isGreyed(0=否 1=是)等于0
        timeout = datetime.now() + timedelta(seconds=120)
        while True:
            api1 = '/api/admin/v1/activiti/getActivitiPage'
            data1 = {
                "assetName": project_name,
                "tradeMode": "02",
                "projectStatusList": [0]}
            res1 = self.post(api1, data1, 0 ,0)['data']['records']
            if res1[0]['isGreyed'] == 0 or datetime.now() > timeout:
                break
            time.sleep(10)
        # 获取意向人信息
        api2 = '/api/admin/v1/openConsult/getSelectEnrollPage'
        data2 = {"assetProjectId": res1[0]['assetProjectId']}
        res2 = self.post(api2, data2, 0 ,0)['data']['records']
        #循环遍历并找出手机号正确的意向人信息
        for i in res2:
            if i['contactPhone'] == str(phone):
                res2 = i
        # 指定意向人
        api3 = '/api/admin/v1/openConsult/auditSelectEnroll'
        data3 = {
            "assetProjectId": res1[0]['assetProjectId'],
            "assetProjectEnrollId": res2['assetProjectEnrollId'],
            "type": 0,
            "remark": None}
        self.post(api3, data3, 0 ,0)
        # 获取项目详情
        api4 = '/api/admin/v1/assetProject/getProjectDetail'
        data4 = {"assetProjectId": res1[0]['assetProjectId'], "type": ""}
        res4 = self.post(api4, data4, 0 ,0)['data']
        # 获取立项信息
        api5 = '/api/admin/v1/assetProject/getProjectApply'
        data5 = {"assetProjectId": res1[0]['assetProjectId'], "type": ""}
        res5 = self.post(api5, data5, 0 ,0)['data']
        # 立项申请
        api = '/api/admin/v1/openConsult/selectApply'
        projectOther = res4['projectOther']
        assetProject = res4['assetProject']
        scheme = res5['scheme']
        data = {
            "detailParamList": [
                res5['details']],
            "fileSaveParams": [
                {
                    "fileType": 1,
                    "fileUrl": "project/441802000000/202303/7203edb8-9586-457c-b602-139f37e7738e.png"},
                {
                    "fileType": 2,
                    "fileUrl": "project/441802000000/202303/353639bb-1371-4295-9010-1914a3fd78f4.doc"}],
            "auditMaterialsSaveParams": [],
            "confirmedPrice": res5['scheme']['floorPrice'],
            "isSubmit": 1,
            "applyType": 2}
        data.update(projectOther)
        data.update(assetProject)
        data.update(scheme)
        self.post(api, data, 0 ,0)

    def bid(self, project_name, phone, account_type, price=100):
        '''公开竞价：出价，price是在原价格上的加价幅度'''
        # 交易大厅数据同步较慢，加个循环去找
        for i in range(0, 6):
            # 按项目名查询assetProjectAuctionId
            api1 = '/api/auction/v1/auctions/open/getMixAuctionPageList'
            data1 = {
                "projectName": project_name,
                "tradeMode": "01",
                "auctionStartTime": str(datetime.now())[0:10],
                "auctionEndTime": str(datetime.now())[0:10],
                "isShowPushData": 1,
                "isShowTestData": 0
            }
            res1 = self.post(api1, data1, phone, account_type)['data']['records']
            if res1 != []:
                break
            print('交易大厅数据同步较慢,正在等待10后重试')
            time.sleep(10)
        # 如果无人出价，只能出跟底价一样的价格，有则需加上底价
        if res1[0]['bid']:
            offerAPrice = float(res1[0]['auctionPrice']) + price
        else:
            offerAPrice = float(res1[0]['floorPrice'])
        # 计算距离竞投开始还有多少时间，并等待竞投开始后才出价
        auctionStartDate = pandas.Timestamp(res1[0]['auctionStartDate'])
        now = pandas.Timestamp(datetime.now())
        if auctionStartDate > now:
            sleep_time = (auctionStartDate - now).total_seconds()
            print(f'项目将在{auctionStartDate}开始竞投，请耐心等待{sleep_time}秒')
            time.sleep(sleep_time + 10)  # 等待竞投开始才出价
        api = '/api/auction/v1/auctions/auction'
        data = {"assetProjectId": res1[0]['assetProjectId'],
                "assetProjectAuctionId": res1[0]['assetProjectAuctionId'],
                "offerAPrice": offerAPrice}
        self.post(api, data, phone, account_type)

    def enroll_audit(self, projectName, phone=None, type=0):
        '''公开竞价：资格审核，只有公开竞价需要资格审核,phone是需要审核的用户账号不传则审核所有,type0通过，1不通过'''
        # 资格审核,待资格审核列表搜索项目
        api1 = '/api/admin/v1/assetProject/getEnrollAuditProject'
        data1 = {"projectName": projectName}
        res1 = self.post(api1, data1, 1, 1)['data']['records']
        if res1[0]['isRequires'] == 1:
            # 搜索竞投人信息
            api2 = '/api/admin/v1/assetProject/getEnrollAuditProjectDetail'
            data2 = {"assetProjectId": res1[0]['assetProjectId']}
            res2 = self.post(api2, data2, 1, 1)['data']['records']
            for i in res2:
                #判断是否有传手机号，有则只审核该手机号，无则审核所有
                if phone:
                    if i['contactPhone'] == str(phone):
                        res2 = i
                #审核
                api = '/api/admin/v1/assetProjectEnroll/audit'
                data = {
                    "assetProjectEnrollId": i['assetProjectEnrollId'],
                    "type": type,
                    "remark": "通过"}
                self.post(api, data, 1, 1)

    def publish_open_notice(self, project_name, enroll_end, auction_start,auction_end):
        '''公开竞价：发布交易公告,enroll_end距离报名截止，auction_end竞投剩余，auction_start报名截止后距竞投开始'''
        # 按项目名搜索待发布的项目信息
        api1 = '/api/admin/v1/activiti/getActivitiPage'
        data1 = {"projectName": project_name, "actNode": 5, "sortField": 2,
               "projectStatusList": [20]}
        res1 = self.post(api1, data1, 1, 1)['data']['records']
        # 获取项目信息
        api2 = '/api/admin/v1/assetProject/getProjectDetail'
        data2 = {"assetProjectId": res1[0]['assetProjectId'], "type": ""}
        res2 = self.post(api2, data2, 1, 1)['data']
        # 发布公告
        api = '/api/admin/v1/assetInformation/saveAssetInformation'
        data = {"taskId": res1[0]['taskId'],
                "businessKey": res1[0]['businessKey'],
                "assetProjectId": res1[0]['assetProjectId'],
                "auctionStartDate": str(
            datetime.now() + timedelta(seconds=auction_start + enroll_end))[0:19],  # 竞投开始
            "auctionEndDate": str(datetime.now() + timedelta(
                seconds=auction_start + auction_end + enroll_end))[0:19],  # 竞投结束
            "contact": res2['assetProject']['contact'],
            "phone": res2['assetProject']['phone'],
            "earnestMoneyPayEndDate": str(datetime.now() + timedelta(seconds=enroll_end))[0:19],
            # 报名截止
            "enrollEndDate": str(datetime.now() + timedelta(seconds=enroll_end))[0:19],
            # 缴纳保证金截止
            "extendSecond": 180,  # 出价延时
            "maxExtend": 999,  # 最大延时次数
            "resultPostPeriod": 5,
            "contractDeadlinePeriod": randint(1, 20),  # 多少日签署合同
            "releaseUser": res2['applyLog'][0]['assignee'],  # 发布机构信息
            "sysOrganizationId": res2['applyLog'][0]['sysOrganizationId'],
            "releaseOrganizationName": res2['applyLog'][0]['organizationName'],
            "releaseDate": str(datetime.now())[0:10] + ' 23:59:59',
            "publicStartDate": str(datetime.now())[0:10],  # 公示期
            "publicEndDate": str(datetime.now() + timedelta(days=randint(1, 100)))[0:10],
            "informationTitle": project_name + "交易公告",  # 公告标题
            "projectName": project_name}
        self.post(api, data, 1, 1)

    def add_contract(self, contract_name, user_type, user_name, card_type, card_no, user_phone):
        '''
        线下续约
        补录线下合同
        contract_name: 合同名
        user_type: 承租方类型1是个人，2是法人
        user_name: 承租方名
        card_type: 证件类型
        card_no: 证件类型，10是身份证，49是信用代码
        user_phone: 证件号
        '''
        contract_end_day = 20  # 合同预计结束天数,目前无法超过月需另写方法
        org_info = self.get_org_info()['data']
        asset_info = self.chooes_asset(self.project_name)[0]
        api = '/api/admin/v1/assetProjectExternalContractHi/saveContractHi'
        data = {
            "organizationId": org_info['organizationId'],
            "organizationName": org_info['organizationName'],
            "provinceId": org_info['organizationProvinceId'],
            "province": org_info['organizationProvince'],
            "cityId": org_info['organizationCityId'],
            "city": org_info['organizationCity'],
            "areaId": org_info['organizationAreaId'],
            "area": org_info['organizationArea'],
            "streetId": org_info['organizationStreetId'],
            "street": org_info['organizationStreet'],
            "address": org_info['organizationAddress'],
            "contact": org_info['tenderUserName'],
            "phone": org_info['tenderUserPhone'],
            "contractName": self.project_name,
            "contractNo": self.project_name,
            "contractStartDate": str(datetime.now())[0:10],
            "contractEndDate": str(datetime.now() + timedelta(days=contract_end_day))[0:10],
            'userType': user_type,  # 承租方类型1个人，2法人
            'userName': user_name,  # 承租方名称
            'idCardType': card_type,  # 证件类型，10是身份证，49是信用代码
            'idCardNo': card_no,  # 证件号
            'userPhone': user_phone,  # 用户手机
            "contractSignPlace": "签订地点广州",
            "clinchAmount": 1,  # 本方金额
            "ourOwnProportion": 100,  # 本方占比
            "totalPrice": "1.00",  # 合同总价
            "tradeType": "01",  # 交易方式 01=出租，02=出让 03=出售，04 = 转让中
            "contractSignDate": "2025-04-25",  # 合同签订日期
            "contractBackdate": True,  # 是否补签合同
            "contractSign": 1,  # 0正常合同，1待核合同
            "firstPayDate": "2025-04-25",  # 支付日期
            "timeLimit": {
                "year": 0,
                "month": 0,
                "day": contract_end_day
            },
            "timeLimitText": f"{contract_end_day}日",  # 合同期限
            "paymentType": 4,  # 结算方式 0=按月 1=按季 2=按半年 3=按年 4=一次性
            "contentRequire": "内容1111",
            "achieve": "达成111",
            'operateType': 1 , # 保存/编辑=1 提交审核=2
            "assetId": asset_info['assetId'],
            "assetCode": asset_info['assetCode'],
            "assetName": asset_info['assetName'],
            "assetType":  asset_info['assetType'],
            "landOccupation": asset_info['landOccupation'],
            "landOccupationUnit":  asset_info['landOccupationUnit'],
            "contractInstallmentDetailParamList": [],
            "adress": asset_info['assetAddress'],#资产地址
            "assetImage": asset_info['mainPicture'],
            "files": self.uoload_contract_files(),
            "reinstallment": False #是否重新分期 否=false 是=true
        }
        self.post(api, data, account_type=0, phone=0)

    def agree_renewal(self, project_name, phone, account_type, status=1):
        '''线下续约：原租方同意,status=0不同意 1同意'''
        # 搜索待确认续约项目
        api = '/api/auction/v1/renewalTrade/open/getRenewalTradePageList'
        data1 = {
            "projectName": project_name,
            "tradeMode": "04",
            "isShowPushData": 1,
            "isShowTestData": "0"
        }
        res1 = self.post(api, data1, phone, account_type)['data']['records']
        # 同意/不同意
        api = '/api/auction/v1/assetProjectEnroll/saveAssetProjectEnroll'
        data = {

            "files": [],
            "status": status,
            "businessKey": res1[0]['businessKey'],
            "auctionPrice": res1[0]['tradePrice'],
            "assetProjectId": res1[0]['assetProjectId']
        }
        self.post(api, data, phone, account_type)

    def publish_notice(self, project_name):
        '''小额简易：发布交易公告'''
        # 按项目名查找待发布公告的小额简易项目
        api1 = '/api/admin/v1/activiti/getActivitiPage'
        data1 = {
            "projectName": project_name,
            "actNode": 5,
            "sortField": 2,
            "projectStatusList": [20]
        }
        res1 = self.post(api1, data1, 1, 1)['data']['records']
        api = '/api/admin/v1/smallTrade/releasePost'
        data = {"taskId": res1[0]['taskId'],
                "businessKey": res1[0]['businessKey'],
                "assetProjectId": res1[0]['assetProjectId'],
                "informationTitle": f"{project_name}交易公告",
                "projectName": project_name,
                "publicStartDate": str(datetime.now())[0:10],  # 公示期
                "publicEndDate": str(datetime.now() + timedelta(days=randint(1, 100)))[0:10],
                "resultPostPeriod": 5,  # 结果公示周期 默认五天
                "contractDeadlinePeriod": randint(1, 20)}  # 合同签订截止周期 默认10天
        self.post(api, data, 1, 1)

    def small_project_job(self, project_name):
        '''小额简易：项目流转到待上传合同状态'''
        api1 = '/api/auction/v1/smallTrade/open/getSmallTradePageList'
        data1 = {"projectName": project_name, "tradeMode": "03"}
        res1 = self.post(api1, data1, 2, 2)['data']['records']
        api = '/api/admin/v1/activiti/smallProjectJobTest'
        data = {"assetProjectId": res1[0]['assetProjectId']}
        self.post(api, data, 0 ,0)


